<!DOCTYPE html>
<html>
<head>
<title>Process Flow</title>
<meta name="description" content="A simple process flow or SCADA diagram editor, simulating equipment monitoring and control." />
<!-- Copyright 1998-2016 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="go.js"></script>
<link href="../assets/css/goSamples.css" rel="stylesheet" type="text/css" />  <!-- you don't need to use this -->
<script src="goSamples.js"></script>  <!-- this is only for the GoJS Samples framework -->
<script id="code">
  function init() {
    if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this
    var $ = go.GraphObject.make;  // for more concise visual tree definitions

    myDiagram =
      $(go.Diagram, "myDiagramDiv",
        {
          "grid.visible": true,
          "grid.gridCellSize": new go.Size(30, 20),
          "draggingTool.isGridSnapEnabled": true,
          "resizingTool.isGridSnapEnabled": true,
          "rotatingTool.snapAngleMultiple": 90,
          "rotatingTool.snapAngleEpsilon": 45,
          "undoManager.isEnabled": true
        });

    // when the document is modified, add a "*" to the title and enable the "Save" button
    myDiagram.addDiagramListener("Modified", function(e) {
      var button = document.getElementById("SaveButton");
      if (button) button.disabled = !myDiagram.isModified;
      var idx = document.title.indexOf("*");
      if (myDiagram.isModified) {
        if (idx < 0) document.title += "*";
      } else {
        if (idx >= 0) document.title = document.title.substr(0, idx);
      }
    });

    myDiagram.nodeTemplateMap.add("Process",
      $(go.Node, "Auto",
        { locationSpot: new go.Spot(0.5, 0.5), locationObjectName: "SHAPE",
          resizable: true, resizeObjectName: "SHAPE" },
        new go.Binding("location", "pos", go.Point.parse).makeTwoWay(go.Point.stringify),
        $(go.Shape, "Cylinder1",
          { name: "SHAPE",
            strokeWidth: 2,
            fill: $(go.Brush, "Linear",
                    { start: go.Spot.Left, end: go.Spot.Right,
                      0: "gray", 0.5: "white", 1: "gray" }),
            minSize: new go.Size(50, 50),
            portId: "", fromSpot: go.Spot.AllSides, toSpot: go.Spot.AllSides
          },
          new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify)),
        $(go.TextBlock,
          { alignment: go.Spot.Center, textAlign: "center", margin: 5,
            editable: true },
          new go.Binding("text").makeTwoWay())
      ));

    myDiagram.nodeTemplateMap.add("Valve",
      $(go.Node, "Vertical",
        { locationSpot: new go.Spot(0.5, 1, 0, -21), locationObjectName: "SHAPE",
          selectionObjectName: "SHAPE", rotatable: true },
        new go.Binding("angle").makeTwoWay(),
        new go.Binding("location", "pos", go.Point.parse).makeTwoWay(go.Point.stringify),
        $(go.TextBlock,
          { alignment: go.Spot.Center, textAlign: "center", margin: 5, editable: true },
          new go.Binding("text").makeTwoWay(),
          // keep the text upright, even when the whole node has been rotated upside down
          new go.Binding("angle", "angle", function(a) { return a === 180 ? 180 : 0; }).ofObject()),
        $(go.Shape,
          { name: "SHAPE",
            geometryString: "F1 M0 0 L40 20 40 0 0 20z M20 10 L20 30 M12 30 L28 30",
            strokeWidth: 2,
            fill: $(go.Brush, "Linear", { 0: "gray", 0.35: "white", 0.7: "gray" }),
            portId: "", fromSpot: new go.Spot(1, 0.35), toSpot: new go.Spot(0, 0.35) })
      ));

    myDiagram.linkTemplate =
      $(go.Link,
        { routing: go.Link.AvoidsNodes, curve: go.Link.JumpGap, corner: 10, reshapable: true, toShortLength: 7 },
        new go.Binding("points").makeTwoWay(),
        // mark each Shape to get the link geometry with isPanelMain: true
        $(go.Shape, { isPanelMain: true, stroke: "black", strokeWidth: 5 }),
        $(go.Shape, { isPanelMain: true, stroke: "gray", strokeWidth: 3 }),
        $(go.Shape, { isPanelMain: true, stroke: "white", strokeWidth: 1, name: "PIPE", strokeDashArray: [10, 10] }),
        $(go.Shape, { toArrow: "Triangle", fill: "black", stroke: null })
      );

    load();

    loop();  // animate some flow through the pipes
  }

  function loop() {
    var diagram = myDiagram;
    setTimeout(function() {
      var oldskips = diagram.skipsUndoManager;
      diagram.skipsUndoManager = true;
      diagram.links.each(function(link) {
          var shape = link.findObject("PIPE");
          var off = shape.strokeDashOffset - 2;
          shape.strokeDashOffset = (off <= 0) ? 20 : off;
        });
      diagram.skipsUndoManager = oldskips;
      loop();
    }, 100);
  }

  function save() {
    document.getElementById("mySavedModel").value = myDiagram.model.toJson();
    myDiagram.isModified = false;
  }
  function load() {
    myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
  }
</script>

</head>
<body onload="init()">
<div id="sample">
  <h3>GoJS Process Flow</h3>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:500px"></div>
  <p>
    A <em>process flow diagram</em> is commonly used in chemical and process engineering to indicate the general flow of plant processes and equipment.
    A simple SCADA diagram, with animation of the flow along the pipes, is implemented here.
  </p>
  <p>
    The diagram displays the background grid layer by setting <b>grid.visible</b> to true,
    and also allows snapping to the grid using <a>DraggingTool.isGridSnapEnabled</a>,
    <a>ResizingTool.isGridSnapEnabled</a>, and <a>RotatingTool.snapAngleMultiple</a> alongside <a>RotatingTool.snapAngleEpsilon</a>.
  </p>
  <p>
    The diagram also uses the <b>loop</b> function to animate the links by adjusting the <a>Shape.strokeDashOffset</a> every 100 ms.
  </p>
  <div>
    <div>
      <button id="SaveButton" onclick="save()">Save</button>
      <button onclick="load()">Load</button>
      Diagram Model saved in JSON format:
    </div>
    <textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
  "nodeDataArray": [
{"key":"P1", "category":"Process", "pos":"150 120", "text":"Process"},
{"key":"P2", "category":"Process", "pos":"330 320", "text":"Tank"},
{"key":"V1", "category":"Valve", "pos":"270 120", "text":"V1"},
{"key":"P3", "category":"Process", "pos":"150 420", "text":"Pump"},
{"key":"V2", "category":"Valve", "pos":"150 280", "text":"VM", "angle":270},
{"key":"V3", "category":"Valve", "pos":"270 420", "text":"V2", "angle":180},
{"key":"P4", "category":"Process", "pos":"450 140", "text":"Reserve Tank"},
{"key":"V4", "category":"Valve", "pos":"390 60", "text":"VA"},
{"key":"V5", "category":"Valve", "pos":"450 260", "text":"VB", "angle":90}
 ],
  "linkDataArray": [
{"from":"P1", "to":"V1"},
{"from":"P3", "to":"V2"},
{"from":"V2", "to":"P1"},
{"from":"P2", "to":"V3"},
{"from":"V3", "to":"P3"},
{"from":"V1", "to":"V4"},
{"from":"V4", "to":"P4"},
{"from":"V1", "to":"P2"},
{"from":"P4", "to":"V5"},
{"from":"V5", "to":"P2"}
 ]}
    </textarea>
  </div>
</div>
</body>
</html>
